/*
*  Copyright (c) 2006-2020, Chukie
*
*  SPDX-License-Identifier: Apache-2.0
*
*  @file     : cellular.h
*
*  @brief    :
*  Website   : https://gitee.com/open-embedded
*
*
*  Change Logs:
*  Date           Author          Notes
*  2020-06-22     zhaocj       The first version
*/
/* Define to prevent recursive inclusion ------------------------------------*/
#ifndef __CELLULAR_H__
#define __CELLULAR_H__
/* Includes -----------------------------------------------------------------*/
#include "cellular_def.h"
#include "cellular_ppp.h"
#ifdef CELLULAR_USING_SMS
#include "cellular_sms.h"
#endif /*CELLULAR_USING_SMS*/
#ifdef __cplusplus
extern "C"
{
#endif /*__cplusplus*/
/* Exported types -----------------------------------------------------------*/
struct sim_card_info
{
    uint8_t ready;
    char imsi[SIM_IMSI_SIZE + 1];
    char iccid[SIM_ICCID_SIZE + 1];
    char number[SIM_NUMBER_SIZE + 1];
} __PACKED;
typedef struct sim_card_info *sim_info_t;

struct cell_access_info
{
    register_state_t reg;
    // network_act_band_t rat_band;          //ganyang modify same as "band" below ?
    network_act_t act;
    /*cell id*/
    uint32_t cellid;
    uint16_t mcc;
    uint16_t mnc;
    uint8_t csq;
    uint8_t biterr;
    int16_t rssi;
    union
    {
        /*GSM/UMTS/LTE LAC/TAC*/
        uint32_t lac;
        /*CDMA NID/sid*/
        struct
        {
            uint32_t nid: 16;
            uint32_t sid: 16;
        } __PACKED nid_sid;
    } __PACKED lac_nid;
    network_act_band_t band;
    char operator_name[32];
    char band_name[32];
} __PACKED;
typedef struct cell_access_info *cell_access_info_t;

struct cellular_sleep_ctrl
{
    /*休眠锁*/
    volatile int wakeup_lock;
    /*唤醒完成量*/
    struct rt_semaphore wakeup_sem;
    /*cellular 唤醒时间戳,单位ms*/
    volatile uint32_t wakeup_ts;
    /*无数据休眠间隔,单位ms*/
    uint16_t sleep_interval;
    /*是否允许休眠*/
    bool enable;
};
typedef struct cellular_sleep_ctrl *cellular_sleep_ctrl_t;

struct module_info
{
    /* cellular状态*/
    module_state_t state;
    /*cellular name*/
    char name[RT_NAME_MAX + 1];
    /*cellular 的IMEI*/
    char imei[MODULE_IMEI_SIZE + 1];
    /*cellular 软件版本*/
    char version[MODULE_VERSION_SIZE + 1];
};
typedef struct module_info *module_info_t;

struct dial_config
{
    struct cellular_ppp_config ppp_cfg;
    //默认承载
    //struct cellular_ppp_config db_cfg;
    char apn[CELLULAR_PPP_MAX_APN_LEN + 1];
    network_mode_t network_mode;
#ifdef CELLULAR_USING_SMS    
    char sms_center[SIM_NUMBER_SIZE + 1];
#endif /*CELLULAR_USING_SMS*/    
} __PACKED;
typedef struct dial_config *dial_config_t;

struct dial_info
{
    /*当前使用的SIM卡*/
    uint8_t used_sim;
    /*当前使用的SIM卡 信息*/
    struct sim_card_info sim_info[CELLULAR_SUPOORT_MAX_SIM_NUM];
    struct dial_config config[CELLULAR_SUPOORT_MAX_SIM_NUM];
} __PACKED;
typedef struct dial_info *dial_info_t;

typedef struct cellular_device *cellular_device_t;

struct celluar_ops
{
    int (*init)(cellular_device_t dev);
    /**
     * @brief control cellular module callback
     *
     * @param dev   pointer to cellular device
     * @param cmd   reference to enum cellular_cmd_t
     * @param port  the port to send control cellular module
     * @param timeout   the timeout in seconds
     * @param args    control args
     * @return int
     *          0: success
     *          !0: error
     */
    int (*control)(cellular_device_t dev, cellular_cmd_t cmd, int port, uint32_t timeout, void *args);
    const char *(*get_port_name)(cellular_device_t dev, int port);
    uint32_t (*get_port_speed)(cellular_device_t dev, int port);
#ifdef CELLULAR_USING_SMS    
    /**
     * @brief send sms
     * 
     * @param dst_number destination phone number
     * @param content   sms content
     * @param length    sms content length
     * @param encoding  sms encoding, reference to enum sms_encode_t
     * @param csca      sms center number
     * 
     * @return int
     *              >=0: success
     *              <0: failed
     */
    int (*sms_send)(char *dst_number, char *content, int length, sms_encode_t encoding, char *csca);
    /**
     * @brief receive sms
     * 
     * @param recv_cb
     * 
     * @return int
     *          >=0: sms index,Can be used as an input parameter of the SMS delete function  
     *          <0: no sms
     */
    int (*sms_recv)(sms_rcv_callback_t recv_cb);
    /**
     * @brief delete sms
     * 
     * @param index     sms index
     * 
     * @return int
     *              >=0: success
     *              <0: failed
     */
    int (*sms_delete)(int index);
#endif /*CELLULAR_USING_SMS*/    
};

/*cellular device*/
struct cellular_device
{
#ifdef CELLULAR_USING_CMUX
    struct cmux cmux;
#endif /*CELLULAR_USING_CMUX*/
    /*cellular info*/
    struct module_info module_info;
    /*拨号信息*/
    struct dial_info dial_info;
    /*当前接入的网络信息*/
    struct cell_access_info cell_info;
#ifdef CELLULAR_USING_SLEEP_CTRL
    /*休眠控制*/
    struct cellular_sleep_ctrl sleep_ctrl;
#endif /*CELLULAR_USING_SLEEP_CTRL*/
    /**
     * @brief Processing status callback function
     * @param status Processing status: 0-failed,!0 success
     * @param args  user args give by user
     *
     * @return int
     *          !0: Interrupt the current process and exit the process
     *          0: Continue the current process
     */
    int (*proc_cb)(int status, void *args);
#ifdef CELLULAR_USING_RING    
    /*振铃回调,可能会在中断中调用,注意该回调的实现*/
    int (*ring_cb)(cellular_device_t  dev, int ind);
#endif /*CELLULAR_USING_RING*/    
    void *usr_args; //user args used in proc_cb
    const struct celluar_ops *ops;
    struct cellular_ppp ppp;
    rt_slist_t list;
};

/* Exported constants -------------------------------------------------------*/
/* Exported macro -----------------------------------------------------------*/
/* Exported functions -------------------------------------------------------*/
/**
 * @brief register cellular device
 *
 * @param dev
 * @return cellular_err_t
 *
 */
int cellular_device_register(cellular_device_t dev, const char *name);

/**
 * @brief
 *
 * @param name
 * @return cellular_device_t
 *          NULL: can't find device
 *          !NULL: the pointer to device
 */

/**
 * @brief find and attach cellular device by name
 *
 * @param name cellular device name
 * @param proc_cb Processing status callback function
 * @param usr_args user args used in proc_cb
 * @return cellular_device_t reference to cellular_device_t
 */
cellular_device_t cellular_find_attach(const char *name, int (*proc_cb)(int status, void *args), void *usr_args);

rt_inline module_state_t cellular_get_module_state(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return dev->module_info.state;
}

rt_inline int cellular_get_used_sim_idx(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return (dev->dial_info.used_sim & 0x01);
}

rt_inline sim_info_t cellular_get_used_sim_info(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return &(dev->dial_info.sim_info[(dev->dial_info.used_sim & 0x01)]);
}

rt_inline cellular_ppp_config_t cellular_get_used_ppp_config(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return &(dev->dial_info.config[(dev->dial_info.used_sim & 0x01)].ppp_cfg);
}

rt_inline dial_config_t cellular_get_used_dial_config(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return &(dev->dial_info.config[(dev->dial_info.used_sim & 0x01)]);
}

#ifdef CELLULAR_USING_SLEEP_CTRL
rt_inline cellular_sleep_ctrl_t cellular_get_sleep_control(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return &(dev->sleep_ctrl);
}
#endif /*CELLULAR_USING_SLEEP_CTRL*/

rt_inline cell_access_info_t cellular_get_cell_access_info(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return &(dev->cell_info);
}

rt_inline module_info_t cellular_get_module_info(cellular_device_t dev)
{
    RT_ASSERT(dev);
    return &(dev->module_info);
}

rt_inline const char *cellular_get_port_name(cellular_device_t dev, cellular_port_type_t port)
{
    RT_ASSERT((dev && dev->ops && dev->ops->get_port_name));
    return dev->ops->get_port_name(dev, port);
}

/* operation interface----------------------------------------------------------------------------*/
/**
 * @brief 控制模组开关机
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args  0: 关机; 1: 开机
 * @return cellular_device_t reference to cellular_device_t
 */
int cellular_module_power(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args);

/**
 * @brief 获取唤醒锁
 *
 * @param dev 设备指针
 * @return cellular_device_t reference to cellular_device_t
 */
int cellular_module_wakeup_lock(cellular_device_t dev);

/**
 * @brief 释放唤醒锁
 *
 * @param dev 设备指针
 * @return cellular_device_t reference to cellular_device_t
 */
int cellular_module_wakeup_unlock(cellular_device_t dev);

/**
 * @brief 休眠模组,该函数会检测唤醒锁，只有所有线程都释放了唤醒锁后才会执行休眠操作
 *
 * @param dev 设备指针
 * @return cellular_device_t reference to cellular_device_t
 */
int cellular_module_sleep(cellular_device_t dev);

/**
 * @brief 控制模组复位
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_module_reset(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    return dev->ops->control(dev, CELLULAR_CMD_RESET, port, timeout, args);
}

/**
 * @brief 初始化模组
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_module_init(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_INIT, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 检测SIM卡
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_check_sim(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_CHECK_SIM, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 配置参数,例如APN、网络模式等
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_module_setup(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_SETUP, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 检查网络状态
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_check_network(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_CHECK_NETWORK, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 控制数据模式
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args  0: 退出数据模式; 1: 进入数据模式
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_data_mode(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_DATA_MODE, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 控制模组开关一次射频
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_switch_rf(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_SWITCH_RF, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 切换SIM卡
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args  0: 切换到SIM1; 1: 切换到SIM2
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_switch_sim(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_SWITCH_SIM, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

/**
 * @brief 控制模组挂断电话
 *
 * @param dev 设备指针
 * @param port 指定下发AT指令所使用的端口
 * @param timeout 操作超时时间,单位秒
 * @param args
 * @return cellular_device_t reference to cellular_device_t
 */
rt_inline int cellular_hangup_call(cellular_device_t dev, cellular_port_type_t port, uint32_t timeout, void *args)
{
    RT_ASSERT((dev && dev->ops && dev->ops->control));
    if (!strlen(cellular_get_port_name(dev, port)))
    {
        return CELLULAR_ERROR;
    }
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->control(dev, CELLULAR_CMD_HANGUP_CALL, port, timeout, args);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

#ifdef CELLULAR_USING_RING
rt_inline void cellular_set_ring_cb(cellular_device_t dev,int (*ring_cb)(cellular_device_t  dev, int ind))
{
    RT_ASSERT((dev));
    dev->ring_cb = ring_cb;
}
#endif /*CELLULAR_USING_RING*/

#ifdef CELLULAR_USING_SMS
rt_inline int cellular_sms_send(cellular_device_t dev,char *dst_number, char *sms_contxt, int length, sms_encode_t encoding)
{
    dial_config_t cfg = cellular_get_used_dial_config(dev);
    RT_ASSERT((dev && dev->ops && dev->ops->sms_send));
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->sms_send(dst_number, sms_contxt, length, encoding, cfg->sms_center);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

rt_inline int cellular_sms_recv(cellular_device_t dev,sms_rcv_callback_t recv_cb)
{
    RT_ASSERT((dev && dev->ops && dev->ops->sms_recv));
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->sms_recv(recv_cb);
    cellular_module_wakeup_unlock(dev);
    return ret;
}

rt_inline int cellular_sms_delete(cellular_device_t dev,int index)
{
    RT_ASSERT((dev && dev->ops && dev->ops->sms_delete));
    cellular_module_wakeup_lock(dev);
    int ret = dev->ops->sms_delete(index);
    cellular_module_wakeup_unlock(dev);
    return ret;
}
#endif /*CELLULAR_USING_SMS*/
/**
 * @brief start ppp
 *
 * @param dev   pointer to cellular device
 * @param echo_interval  lcp echo interval in seconds
 * @param user_hook     user callback
 * @return int
 */
int cellular_ppp_start(cellular_device_t dev, uint8_t echo_interval, void (*user_hook)(int status, int event));
int cellular_ppp_stop(cellular_device_t dev);
int cellular_ppp_get_used_auth(cellular_device_t dev);
uint32_t cellular_ppp_get_ip(cellular_device_t dev);
uint32_t cellular_ppp_get_tx_bytes(cellular_device_t dev);
uint32_t cellular_ppp_get_rx_bytes(cellular_device_t dev);
int cellular_ppp_get_state(cellular_device_t dev);
const char *cellular_ppp_auth2str(ppp_auth_type_t auth);
const char *cellular_network_mode2str(network_mode_t mode);

const char *cellular_reg_state_2_str(register_state_t state);
const char *cellar_band_2_str(network_act_band_t band);
network_act_band_t cellular_arfcn_2_band(const char *act, int arfcn);

#ifdef __cplusplus
{
#endif /*__cplusplus*/
#endif /*__CELLULAR_H__*/
    /* End of file****************************************************************/

